/*
 * Copyright (C) 2022 KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */

#include "desktopitemhelper.h"
#include <QVector>
#include <QSettings>
#include <KDesktopFile>
#include <KConfigGroup>
#include "iconprovider.h"

Q_GLOBAL_STATIC_WITH_ARGS(QVector<QStringList>, categoriesVector, ({
    { "All" },
    { "Android" },
    { "Network", "network" },
    { "Messaging", "social"},
    { "Audio", "Video", "video" },
    { "Development", "develop" },
    { "Graphics", "graphic" },
    { "Game", "game" },
    { "Office", "Calculator", "Spreadsheet" , "Presentation",
      "WordProcessor" , "TextEditor", "office" },
    { "Education", "education" },
    { "System" , "Settings", "Security", "system" },
    { "safe" }
}))

static QString appCategories(const KDesktopFile &kDesktopFile, const UKUI::CategoryStringMap &categoryMap)
{
    QString categories;
    KConfigGroup desktopGroup =  kDesktopFile.desktopGroup();
    QString desktopfpExecName = QSettings(kDesktopFile.fileName(), QSettings::IniFormat).value("Desktop Entry/Exec").toString();
    desktopfpExecName = desktopfpExecName.mid(desktopfpExecName.lastIndexOf("/") + 1);
    desktopfpExecName = desktopfpExecName.left(desktopfpExecName.lastIndexOf(" "));
    if (categoryMap.contains(desktopfpExecName)) {
        categories = categoryMap[desktopfpExecName];
    } else {
        categories = desktopGroup.readEntry("Categories");
    }
    qDebug() << __func__ << categories;
    return categories;
}

static QString iconIdWithInstalledApp(const QString &iconName)
{
    return IconProvider::iconIdWithExistedApp(iconName);
}

static QString iconIdWithInstallingApp(const KDesktopFile &kDesktopFile, const QString &appCategories)
{
    QString iconId;
    QString iconName = kDesktopFile.readIcon();
    bool isAndroidApp = appCategories.contains("Android", Qt::CaseInsensitive);
    iconId = isAndroidApp ? IconProvider::iconIdWithInstallingAndroidApp(iconName)
                          : IconProvider::iconIdWithInstallingApp(iconName, kDesktopFile.fileName());
    return iconId;
}

DesktopItemHelper::DesktopItemHelper()
{

}

UKUI::CategoryIntList DesktopItemHelper::matchingAppCategories(QString category)
{
    QList<int> itemCategories;
    bool findCategory = false;
    for (int j = 0; j < categoriesVector->count(); j++) {
        for (int i = 0; i < categoriesVector->at(j).count(); i++) {
            if (category.contains(categoriesVector->at(j).at(i), Qt::CaseInsensitive)) {
                itemCategories.append(j);
                findCategory = true;
                break;
            }
        }
    }
    if (!findCategory) {
        itemCategories.append(categoriesVector->size());
    }
    return itemCategories;
}

UKUI::ItemId DesktopItemHelper::calculateIdForNewItem(const QList<UKUI::ItemId> &oldIds)
{
    for (int i=0; i<oldIds.count(); ++i) {
        if (i > 0 && oldIds.at(i) - oldIds.at(i-1) > 1) {
            return oldIds.at(i) - 1;
        }
    }
    return oldIds.count() + 1;
}

UKUI::BaseItem * DesktopItemHelper::dissectInstalledDesktopFileToItem(const QString &desktopFile,
                                                          const UKUI::CategoryStringMap &categoryMap,
                                                          const QList<UKUI::ItemId> &existedIds)
{
    using namespace UKUI;
    KDesktopFile kDesktopFile(desktopFile);
    QString categories = appCategories(kDesktopFile, categoryMap);
    QString iconId = iconIdWithInstalledApp(kDesktopFile.readIcon());
    QList<int> actualCategories = matchingAppCategories(categories);
    int id = calculateIdForNewItem(existedIds);
    BaseItem *baseItem = new UKUI::IconItem(
                desktopFile, kDesktopFile.readName(),
                id, iconId, actualCategories);
    return baseItem;
}

UKUI::BaseItem *DesktopItemHelper::dissectInstallingDesktopFileToItem(const QString &desktopFile,
                                                                      const UKUI::CategoryStringMap &categoryMap,
                                                                      const QList<UKUI::ItemId> &existedIds)
{
    using namespace UKUI;
    KDesktopFile kDesktopFile(desktopFile);
    QString categories = appCategories(kDesktopFile, categoryMap);
    QString iconId = iconIdWithInstallingApp(kDesktopFile, categories);
    QList<int> actualCategories = matchingAppCategories(categories);
    qDebug() << __func__ << actualCategories;
    int id = calculateIdForNewItem(existedIds);
    BaseItem *baseItem = new UKUI::IconItem(
                desktopFile, kDesktopFile.readName(),
                id, iconId, actualCategories);

    return baseItem;
}

UKUI::ItemId DesktopItemHelper::appIdByDesktopFromAllItems(const QString &desktopFile, const UKUI::ItemMap &allItems)
{
    if (desktopFile.isEmpty()) {
        return -1;
    }
    auto it = allItems.constBegin();
    while (it != allItems.constEnd()) {
        UKUI::IconItem *iconItem = dynamic_cast<UKUI::IconItem *>(it.value());
        if (iconItem) {
            if (iconItem->getDesktopName() == desktopFile) {
                return it.key();
            }
            auto tmp = iconItem->getDesktopName().split('/');
            if (tmp.isEmpty()) {
                ++ it;
                continue;
            }
            if (tmp.constLast() == desktopFile) {
                return it.key();
            }
        }
        ++ it;
    }
    return -1;
}

UKUI::ItemId DesktopItemHelper::appIdByNameFromAllItems(const QString &name, const UKUI::ItemMap &allItems)
{
    if (name.isEmpty()) {
        return -1;
    }
    auto it = allItems.constBegin();
    while (it != allItems.constEnd()) {
        UKUI::Widget *widget = dynamic_cast<UKUI::Widget *>(it.value());
        if (widget && widget->getName() == name) {
            return it.key();
        }
        ++ it;
    }
    return -1;
}

QList<UKUI::ItemId> DesktopItemHelper::appIdsByDesktopFromAllItems(const QStringList &desktopFiles, const UKUI::ItemMap &allItems)
{
    QList<UKUI::ItemId> appIds;
    for (auto &desktopFile : qAsConst(desktopFiles)) {
        appIds.append(appIdByDesktopFromAllItems(desktopFile, allItems));
    }
    return appIds;
}

bool DesktopItemHelper::itemExistsInSet(UKUI::ItemId itemId, const UKUI::ItemIdInSetMap &itemIdsInSet)
{
    bool existed;
    int setId, pageIndex;
    std::tie(existed, setId, pageIndex) =
            itemPositionInSet(itemId, itemIdsInSet);
    return existed;
}

std::tuple<bool, UKUI::ItemId, int> DesktopItemHelper::itemPositionInSet(int itemId, const UKUI::ItemIdInSetMap &itemIdsInSet)
{
    auto it = itemIdsInSet.constBegin();
    while (it != itemIdsInSet.constEnd()) {
        auto itemsIdsInPage = it.value();
        for (int page=0; page<itemsIdsInPage.count(); ++page) {
            if (itemsIdsInPage.at(page).contains(itemId)) {
                return { true, it.key(), page };
            }
        }
        ++ it;
    }
    return { false, -1, -1 };
}
